function toNumber(x) {
  const n = Number(x);
  if (!Number.isFinite(n)) throw new Error("NaN");
  return n;
}

function factorial(n) {
  n = Math.floor(toNumber(n));
  if (n < 0) throw new Error("neg");
  let r = 1;
  for (let i = 2; i <= n; i++) r *= i;
  return r;
}

export class ScientificCalculator {
  constructor(root) {
    this.root = root;
    this.render();
  }
  render() {
    this.root.innerHTML = `
      <div class="display" id="sciDisplay">0</div>
      <div class="grid cols-4">
        <button class="key" data-k="sin">sin</button>
        <button class="key" data-k="cos">cos</button>
        <button class="key" data-k="tan">tan</button>
        <button class="key" data-k="pi">π</button>
        <button class="key" data-k="log">log</button>
        <button class="key" data-k="ln">ln</button>
        <button class="key" data-k="^">x^y</button>
        <button class="key" data-k="sqrt">√</button>
        <button class="key" data-k="!">n!</button>
        <button class="key" data-k="exp">exp</button>
        <button class="key" data-k="(">(</button>
        <button class="key" data-k=")">)</button>
        <button class="key" data-k="C">C</button>
        <button class="key" data-k="BS">⌫</button>
        <button class="key" data-k="/">÷</button>
        <button class="key" data-k="*">×</button>
        <button class="key" data-k="7">7</button>
        <button class="key" data-k="8">8</button>
        <button class="key" data-k="9">9</button>
        <button class="key" data-k="-">−</button>
        <button class="key" data-k="4">4</button>
        <button class="key" data-k="5">5</button>
        <button class="key" data-k="6">6</button>
        <button class="key" data-k="+">+</button>
        <button class="key" data-k="1">1</button>
        <button class="key" data-k="2">2</button>
        <button class="key" data-k="3">3</button>
        <button class="key primary" data-k="=">=</button>
        <button class="key" style="grid-column: span 2" data-k="0">0</button>
        <button class="key" data-k=",">,</button>
        <button class="key" data-k=".">.</button>
      </div>
    `;
    this.display = this.root.querySelector("#sciDisplay");
    this.expr = "";
    this.root
      .querySelectorAll(".key")
      .forEach((btn) =>
        btn.addEventListener("click", () => this.onKey(btn.dataset.k))
      );
  }
  onKey(k) {
    if (k === "C") {
      this.expr = "";
      return this.update();
    }
    if (k === "BS") {
      this.expr = this.expr.slice(0, -1);
      return this.update();
    }
    if (k === "=") {
      return this.evaluate();
    }
    if (k === "pi") {
      this.expr += "Math.PI";
      return this.update();
    }
    if (
      k === "sin" ||
      k === "cos" ||
      k === "tan" ||
      k === "log" ||
      k === "ln" ||
      k === "sqrt" ||
      k === "exp"
    ) {
      const map = {
        log: "Math.log10",
        ln: "Math.log",
        sqrt: "Math.sqrt",
        exp: "Math.exp",
      };
      const fn = map[k] || `Math.${k}`;
      this.expr += `${fn}(`;
      return this.update();
    }
    if (k === "!") {
      this.expr += "factorial(";
      return this.update();
    }
    if (k === "^") {
      this.expr += "**";
      return this.update();
    }
    if (k === ",") {
      this.expr += ",";
      return this.update();
    }
    this.expr += k;
    this.update();
  }
  update() {
    this.display.textContent = this.expr || "0";
  }
  evaluate() {
    try {
      const value = Function(
        "factorial",
        `"use strict"; return (${this.expr})`
      )(factorial);
      this.expr = String(value);
      this.update();
    } catch (_e) {
      this.display.textContent = "Error";
    }
  }
}
